/* eslint-env jquery */
/* eslint-disable new-cap */

'use strict'

var $ = jQuery

$.jqPaginator = function (el, options) {
    if (!(this instanceof $.jqPaginator)) {
        return new $.jqPaginator(el, options)
    }

    var self = this

    self.$container = $(el)

    self.$container.data('jqPaginator', self)

    self.init = function () {
        if (options.first || options.prev || options.next || options.last || options.page) {
            options = $.extend(
                {},
                {
                    first: '',
                    prev: '',
                    next: '',
                    last: '',
                    page: ''
                },
                options
            )
        }

        self.options = $.extend({}, $.jqPaginator.defaultOptions, options)

        self.verify()

        self.extendJquery()

        self.render()

        self.fireEvent(this.options.currentPage, 'init')
    }

    self.verify = function () {
        var opts = self.options

        if (!self.isNumber(opts.totalPages)) {
            throw new Error('[jqPaginator] type error: totalPages')
        }

        if (!self.isNumber(opts.totalCounts)) {
            throw new Error('[jqPaginator] type error: totalCounts')
        }

        if (!self.isNumber(opts.pageSize)) {
            throw new Error('[jqPaginator] type error: pageSize')
        }

        if (!self.isNumber(opts.currentPage)) {
            throw new Error('[jqPaginator] type error: currentPage')
        }

        if (!self.isNumber(opts.visiblePages)) {
            throw new Error('[jqPaginator] type error: visiblePages')
        }

        if (!opts.totalPages && !opts.totalCounts) {
            throw new Error('[jqPaginator] totalCounts or totalPages is required')
        }

        if (!opts.totalPages && opts.totalCounts && !opts.pageSize) {
            throw new Error('[jqPaginator] pageSize is required')
        }

        if (opts.totalCounts && opts.pageSize) {
            opts.totalPages = Math.ceil(opts.totalCounts / opts.pageSize)
        }

        if (opts.currentPage < 1 || opts.currentPage > opts.totalPages) {
            throw new Error('[jqPaginator] currentPage is incorrect')
        }

        if (opts.totalPages < 1) {
            throw new Error('[jqPaginator] totalPages cannot be less currentPage')
        }
    }

    self.extendJquery = function () {
        $.fn.jqPaginatorHTML = function (s) {
            return s
                ? this.before(s).remove()
                : $('<p>')
                    .append(this.eq(0).clone())
                    .html()
        }
    }

    self.render = function () {
        self.renderHtml()
        self.setStatus()
        self.bindEvents()
    }

    self.renderHtml = function () {
        var html = []

        var pages = self.getPages()
        for (var i = 0, j = pages.length; i < j; i++) {
            html.push(self.buildItem('page', pages[i]))
        }

        self.isEnable('prev') && html.unshift(self.buildItem('prev', self.options.currentPage - 1))
        self.isEnable('first') && html.unshift(self.buildItem('first', 1))
        self.isEnable('statistics') && html.unshift(self.buildItem('statistics'))
        self.isEnable('next') && html.push(self.buildItem('next', self.options.currentPage + 1))
        self.isEnable('last') && html.push(self.buildItem('last', self.options.totalPages))

        if (self.options.wrapper) {
            self.$container.html(
                $(self.options.wrapper)
                    .html(html.join(''))
                    .jqPaginatorHTML()
            )
        } else {
            self.$container.html(html.join(''))
        }
    }

    self.buildItem = function (type, pageData) {
        var html = self.options[type]
            .replace(/{{page}}/g, pageData)
            .replace(/{{totalPages}}/g, self.options.totalPages)
            .replace(/{{totalCounts}}/g, self.options.totalCounts)

        return $(html)
            .attr({
                'jp-role': type,
                'jp-data': pageData
            })
            .jqPaginatorHTML()
    }

    self.setStatus = function () {
        var options = self.options

        if (!self.isEnable('first') || options.currentPage === 1) {
            $('[jp-role=first]', self.$container).addClass(options.disableClass)
        }
        if (!self.isEnable('prev') || options.currentPage === 1) {
            $('[jp-role=prev]', self.$container).addClass(options.disableClass)
        }
        if (!self.isEnable('next') || options.currentPage >= options.totalPages) {
            $('[jp-role=next]', self.$container).addClass(options.disableClass)
        }
        if (!self.isEnable('last') || options.currentPage >= options.totalPages) {
            $('[jp-role=last]', self.$container).addClass(options.disableClass)
        }

        $('[jp-role=page]', self.$container).removeClass(options.activeClass)
        $('[jp-role=page][jp-data=' + options.currentPage + ']', self.$container).addClass(options.activeClass)
    }

    self.getPages = function () {
        var pages = []

        var visiblePages = self.options.visiblePages

        var currentPage = self.options.currentPage

        var totalPages = self.options.totalPages

        if (visiblePages > totalPages) {
            visiblePages = totalPages
        }

        var half = Math.floor(visiblePages / 2)
        var start = currentPage - half + 1 - (visiblePages % 2)
        var end = currentPage + half

        if (start < 1) {
            start = 1
            end = visiblePages
        }
        if (end > totalPages) {
            end = totalPages
            start = 1 + totalPages - visiblePages
        }

        var itPage = start
        while (itPage <= end) {
            pages.push(itPage)
            itPage++
        }

        return pages
    }

    self.isNumber = function (value) {
        var type = typeof value
        return type === 'number' || type === 'undefined'
    }

    self.isEnable = function (type) {
        return self.options[type] && typeof self.options[type] === 'string'
    }

    self.switchPage = function (pageIndex) {
        self.options.currentPage = pageIndex
        self.render()
    }

    self.fireEvent = function (pageIndex, type) {
        return typeof self.options.onPageChange !== 'function' || self.options.onPageChange(pageIndex, type) !== false
    }

    self.callMethod = function (method, options) {
        switch (method) {
            case 'option':
                self.options = $.extend({}, self.options, options)
                self.verify()
                self.render()
                break
            case 'destroy':
                self.$container.empty()
                self.$container.removeData('jqPaginator')
                break
            default:
                throw new Error('[jqPaginator] method "' + method + '" does not exist')
        }

        return self.$container
    }

    self.bindEvents = function () {
        var opts = self.options

        self.$container.off()
        self.$container.on('click', '[jp-role]', function () {
            var $el = $(this)
            if ($el.hasClass(opts.disableClass) || $el.hasClass(opts.activeClass)) {
                return
            }

            var pageIndex = +$el.attr('jp-data')
            if (self.fireEvent(pageIndex, 'change')) {
                self.switchPage(pageIndex)
            }
        })
    }

    self.init()

    return self.$container
}

$.jqPaginator.defaultOptions = {
    wrapper: '',
    first: '<li class="first"><a href="javascript:;">First</a></li>',
    prev: '<li class="prev"><a href="javascript:;">Previous</a></li>',
    next: '<li class="next"><a href="javascript:;">Next</a></li>',
    last: '<li class="last"><a href="javascript:;">Last</a></li>',
    page: '<li class="page"><a href="javascript:;">{{page}}</a></li>',
    totalPages: 0,
    totalCounts: 0,
    pageSize: 0,
    currentPage: 1,
    visiblePages: 7,
    disableClass: 'disabled',
    activeClass: 'active',
    onPageChange: null
}

$.fn.jqPaginator = function () {
    var self = this

    var args = Array.prototype.slice.call(arguments)

    if (typeof args[0] === 'string') {
        var $instance = $(self).data('jqPaginator')
        if (!$instance) {
            throw new Error('[jqPaginator] the element is not instantiated')
        } else {
            return $instance.callMethod(args[0], args[1])
        }
    } else {
        return new $.jqPaginator(this, args[0])
    }
}
